"""
    涉及城市最近半年的二手房房价文章（房价5）
"""

import datetime
import os
import random
import re
import time
import traceback
from typing import Dict

from langchain import PromptTemplate


try:
    from bot.openai_bot import OpenAIBot
    from conf.config import logger, BASE_DIR, config
    from db.house_price_dao import HousePriceDAO
    from prompts.prompt_templates import ABSTRACT_AND_KEYWORDS_PROMPT, PARAGRAPH1_PROMPT, PARAGRAPH2_PROMPT, PARAGRAPH3_PROMPT
    from prompts.prompt_templates_within_house_price import HOUSE_PRICE5_INFO_PROMPT, HOUSE_PRICE5_INFO_BASE
    from utils.constants import HOUSE_TYPE
    from utils.format_article import count_words, all_has_tags, remove_stop_words, remove_duplicate_paragraph_from_content, \
        extract_abstract_and_keywords
    from utils.laod_data import load_excel_sheet, get_city_name, load_title_list, get_first_day_of_several_months_ago
    from utils.output_file import output_txt, output_json
except ModuleNotFoundError:
    import os
    import sys
    sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))  # 离开IDE也能正常导入自己定义的包
    from bot.openai_bot import OpenAIBot
    from conf.config import logger, BASE_DIR, config
    from db.house_price_dao import HousePriceDAO
    from prompts.prompt_templates import ABSTRACT_AND_KEYWORDS_PROMPT, PARAGRAPH1_PROMPT, PARAGRAPH2_PROMPT, PARAGRAPH3_PROMPT
    from prompts.prompt_templates_within_house_price import HOUSE_PRICE5_INFO_PROMPT, HOUSE_PRICE5_INFO_BASE
    from utils.constants import HOUSE_TYPE
    from utils.format_article import count_words, all_has_tags, remove_stop_words, remove_duplicate_paragraph_from_content, \
        extract_abstract_and_keywords
    from utils.laod_data import load_excel_sheet, get_city_name, load_title_list, get_first_day_of_several_months_ago
    from utils.output_file import output_txt, output_json


def format_house_price_info(house_price_info: Dict) -> str:
    """
    格式化房价信息
    :param house_price_info: 房价信息
    :return: 格式化后的字符串
    """
    house_price_info_base = HOUSE_PRICE5_INFO_BASE

    for index, item in enumerate(house_price_info):
        data = item.get("ctime").strftime("%Y-%m")
        house_price = item.get("house_price")

        house_price_info_base = ''.join([house_price_info_base, f'| {data}', f'| {house_price} |', '\n'])

    return house_price_info_base


def generate_article(prompt, filename, title, city_name):
    """
    生产文章
    :param prompt: 提交gpt的prompt
    :param filename: 保存的文件名
    :param title: 文章标题
    :param city_name: 文章所属的城市名
    :return:
    """
    data = dict()
    openai_bot = OpenAIBot(session_id=filename)
    prompt2 = PARAGRAPH1_PROMPT
    prompt3 = PARAGRAPH2_PROMPT
    prompt4 = PARAGRAPH3_PROMPT
    prompt5 = ABSTRACT_AND_KEYWORDS_PROMPT
    answer1 = openai_bot.reply(question=prompt, session_round=1)
    answer2 = openai_bot.reply(question=prompt2, session_round=2)
    answer3 = openai_bot.reply(question=prompt3, session_round=3)
    answer4 = openai_bot.reply(question=prompt4, session_round=4)
    answer5 = openai_bot.reply(question=prompt5, session_round=5)

    # 提取小标题
    pattern = r'<p><strong>.*?</strong></p>'
    subtitles = re.findall(pattern, answer1)
    # 正文段落
    paragraphs = [answer2, answer3, answer4]

    # 每段正文的第一段与小标题一致，则应该删除正文的第一段
    paragraphs = remove_duplicate_paragraph_from_content(subtitles=subtitles, paragraphs=paragraphs)

    # 带停用词的文章
    content_within_stop_words = "\n".join([subtitles[0], paragraphs[0], subtitles[1], paragraphs[1], subtitles[2], paragraphs[2], ])

    # 删除小标题的停用词
    for index, subtitle in enumerate(subtitles):
        subtitles[index] = remove_stop_words(text=subtitle, stop_words=config.get("stop_words_subtitle"))
    # 删除正文的停用词
    for index, paragraph in enumerate(paragraphs):
        paragraphs[index] = remove_stop_words(text=paragraph, stop_words=config.get("stop_words"))

    # 不带停用词的文章
    content = "\n".join([subtitles[0], paragraphs[0], subtitles[1], paragraphs[1], subtitles[2], paragraphs[2], ])

    abstract, keywords = extract_abstract_and_keywords(answer5)
    data.update({
        "id": str(filename), "title": title, "city_name": city_name,
        "abstract": abstract, "keywords": keywords, "content": content
    })

    # 判断摘要字数是否在限值内
    abstract_word_count = count_words(data.get("abstract"), excluded_char_ist=[])
    if abstract_word_count > config.get("word_limit_for_abstract"):
        raise ValueError(f"摘要字数超限值，共{abstract_word_count}字")

    # 判断正文字数是否足够
    num = count_words(data.get("content"), excluded_char_ist=config.get("excluded_char_ist"))
    if num <= config.get("word_limit_for_content").get("article_with_new_house_price"):
        raise ValueError(f"正文数字不足，只有{num}字")

    # 判断正文是否都有标签
    if not all_has_tags(text=data.get("content"), startswith_tags=config.get("startswith_tags"),
                        endswith_tags=config.get("endswith_tags")):
        raise ValueError(f"正文有些段落标签错误")

    # 输出到json(中文为不可读的Unicode编码)
    output_json(
        output_full_path=os.path.join(BASE_DIR, f"output/{filename}.json"),
        data=data, ensure_ascii=True
    )

    # prompt输出到txt文件
    output_txt(
        output_full_path=os.path.join(BASE_DIR, f'output/{filename}_prompt.txt'),
        txt="\n".join([prompt, prompt2, prompt3, prompt4, prompt5])
    )

    # 文章结果输出到txt文件
    text = "\n\n".join([answer1, answer2, answer3, answer4, answer5])
    count = count_words(text, excluded_char_ist=config.get("excluded_char_ist"))
    text = f"{text}\n\n==========\n正文字数统计：{count}"
    output_txt(
        output_full_path=os.path.join(BASE_DIR, f"output/{filename}.txt"),
        txt=text
    )

    # content_within_stop_words输出到txt文件
    output_txt(
        output_full_path=os.path.join(BASE_DIR, f'output/{filename}_content_within_stop_words.txt'),
        txt=content_within_stop_words
    )


def main():
    fail_list = list()
    title_list = load_title_list()
    for item in title_list:
        filename = item.get("filename")
        title = item.get("title")
        city_name = item.get("city_name")
        try:
            # 从城市名中获取标题名
            city_name = get_city_name(title, original_city_name=city_name)
            if not city_name:
                logger.warning(f'文件名：{filename} 标题：{title} 未识别到城市名')
                raise ValueError("未识别到城市名")

            # 查询数据库获取房价信息
            house_price_dao = HousePriceDAO()
            # 查询城市的区域id
            district_id = house_price_dao.get_dtid_form_city_name(city_name)

            # 上个月一号的日期
            first_day_of_last_month = get_first_day_of_several_months_ago(months=1)
            # 6个月前一号的日期
            first_day_of_6_months_ago = get_first_day_of_several_months_ago(months=6)

            city_second_hand_house_price_info = house_price_dao.get_city_house_price2(
                start_date=first_day_of_6_months_ago, end_date=first_day_of_last_month,
                district_id=district_id, house_type=HOUSE_TYPE["second_hand_house"]
            )

            # 将历史房价信息格式化为字符串
            house_price_info = format_house_price_info(city_second_hand_house_price_info)

            # 渲染模板
            prompt = PromptTemplate(
                input_variables=["title", "city_name", "house_price_info", "date"],
                template=HOUSE_PRICE5_INFO_PROMPT,
                template_format="jinja2"
            )

            prompt_formatted = prompt.format(
                title=title, city_name=city_name, house_price_info=house_price_info,
                date=datetime.date.today().strftime('%Y-%m-%d')
            )

            generate_article(prompt=prompt_formatted, filename=f'{filename}', title=title, city_name=city_name)
            time.sleep(random.uniform(10, 15))

        except Exception:
            error_str = traceback.format_exc()
            logger.error(error_str)
            fail_list.append({"filename": filename, "log": error_str})

    logger.warning(f'失败列表：\n{[item.get("filename") for item in fail_list]}')
    logger.warning(f'失败列表详情：\n{fail_list}')


if __name__ == '__main__':
    main()
